/******************************************************************************
 *
 * Copyright:
 *    (C) 2005 Embedded Artists AB
 *
 * File:
 *    irqUart.c
 *
 * Description:
 *    Sample irq code, that must be compiled in ARM code.
 *
 *****************************************************************************/

/******************************************************************************
 * Includes
 *****************************************************************************/
#include "../general.h"
#include <lpc2xxx.h>
#include "irq_uart.h"
#include "../uart.h"

/*****************************************************************************
 * Local variables
 ****************************************************************************/
uart_t *uart0ptr = NULL;
uart_t *uart1ptr = NULL;

/*****************************************************************************
 * Public function prototypes
 ****************************************************************************/
void uart0ISR(void) __attribute__ ((interrupt));
void uart1ISR(void) __attribute__ ((interrupt));


/*****************************************************************************
 * Common for UART0/1 ISR
 ****************************************************************************/
static void uartISR(uart_t *u) 
{
    volatile tU8  statusReg;
    volatile tU8  dummy;
    volatile tU32 tmpHead;
    volatile tU32 tmpTail;

    while (u) {
        //loop until not more interrupt sources
        statusReg = REG(u->uartRegBase + UIIR);
        if (statusReg & 0x01)
            break;

        //identify and process the highest priority interrupt
        switch (statusReg & 0x0E) {
            case 0x06:  //Receive Line Status
                dummy = REG(u->uartRegBase + ULSR);  //read LSR to clear bits
                break;

            case 0x0c:  //Character Timeout Indicator
            case 0x04:  //Receive Data Available
                do {
                    tmpHead     = (u->uartRxHead + 1) & RX_BUFFER_MASK;
                    u->uartRxHead = tmpHead;

                    if(tmpHead == u->uartRxTail)
                        tmpHead = REG(u->uartRegBase + URBR);  //dummy read to reset IRQ flag
                    else
                        u->uartRxBuf[tmpHead] = REG(u->uartRegBase + URBR);  //will reset IRQ flag
                } while (REG(u->uartRegBase + ULSR) & 0x01);
                break;

            case 0x02:  //Transmit Holding Register Empty
                //check if all data is transmitted
                if (u->uartTxHead != u->uartTxTail) {
                    tU32 bytesToSend;

                    if(statusReg & 0xc0)
                        bytesToSend = 16;    //FIFO enabled
                    else
                        bytesToSend = 1;     //no FIFO enabled

                    do {
                        //calculate buffer index
                        tmpTail = (u->uartTxTail + 1) & TX_BUFFER_MASK;

                        u->uartTxTail = tmpTail;
                        REG(u->uartRegBase + UTHR) = u->uartTxBuf[tmpTail]; 
                    } while ((u->uartTxHead != u->uartTxTail) && --bytesToSend);
                }

                //all data has been transmitted
                else {
                    u->uartTxRunning = FALSE;
                    REG(u->uartRegBase + UIER) &= ~0x02;        //disable TX IRQ
                }
                break;

            default:  //unknown
                dummy = REG(u->uartRegBase + ULSR);
                dummy = REG(u->uartRegBase + URBR);
                break;
        }
    }

    VICVectAddr = 0x00000000;    //dummy write to VIC to signal end of interrupt
}
/*****************************************************************************
 *
 * Description:
 *    Actual uart #0 ISR that is called whenever the uart generated an interrupt.
 *
 ****************************************************************************/
void uart0ISR(void) 
{
    uartISR(uart0ptr);
}

/*****************************************************************************
 *
 * Description:
 *    Actual uart #0 ISR that is called whenever the uart generated an interrupt.
 *
 ****************************************************************************/
void uart1ISR(void) 
{
    uartISR(uart1ptr);
}

/*****************************************************************************
 *
 * Description:
 *    Disable interrupts 
 *
 * Returns:
 *    The current status register, before disabling interrupts. 
 *
 ****************************************************************************/
tU32 disIrq(void) 
{
    tU32 returnReg;

    asm volatile ("disIrq1: mrs %0, cpsr  \n\t"
                  "orr r1, %0, #0xC0      \n\t"
                  "msr cpsr_c, r1         \n\t"
                  "mrs r1, cpsr           \n\t"
                  "and r1, r1, #0xC0      \n\t"
                  "cmp r1, #0xC0          \n\t"
                  "bne disIrq1            \n\t"
                  : "=r"(returnReg)
                  :
                  : "r1"
                 );
    return returnReg;
}

/*****************************************************************************
 *
 * Description:
 *    Restore interrupt state. 
 *
 * Params:
 *    [in] restoreValue - The value of the new status register. 
 *
 ****************************************************************************/
void restoreIrq(tU32 restoreValue) 
{
    asm volatile ("msr cpsr_c, %0  \n\t"
                  :
                  : "r" (restoreValue)
                  : "r1"
                 );
}
